#pragma once

#include<iostream>
#include<cstdio>
#include<pthread.h>
#include<queue>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t producer_cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t consumer_cond = PTHREAD_COND_INITIALIZER;

template<typename T>
class BlockQueue
{
public:
    bool IsFull()
    {
        return _block_queue.size() == _cap;
    }

    bool IsEmpty()
    {
        return _block_queue.empty();
    }

    BlockQueue(int cap)
        :_cap(cap), _mutex(mutex),_producer_cond(producer_cond)
        ,_consumer_cond(consumer_cond),_wait_consumer(0),_wait_producer(0)
    { }

    ~BlockQueue()
    { 
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_producer_cond);
        pthread_cond_destroy(&_consumer_cond);
    } 

    void EnQueue(T& in)
    {
        //互斥锁确保了生产者之间互斥关系、生产者消费者之间互斥关系
        pthread_mutex_lock(&_mutex);
        //即使发生了伪唤醒，线程也会重新检查条件是否真正满足，确保条件真正满足后再继续执行
        while(IsFull())
        //if(IsFull) 防止伪唤醒,非法访问,程序崩溃
        {
            ++_wait_producer;
            pthread_cond_wait(&_producer_cond, &_mutex);
            --_wait_producer;
        }
        //进行生产
        _block_queue.push(in);
        //有数据了,消费者可以来消费了
        if(_wait_consumer > 0)
            pthread_cond_signal(&_consumer_cond);
        pthread_mutex_unlock(&_mutex);
        return ;
    }

    void PopQueue(T* out)
    {
        //互斥锁确保了消费者之间互斥关系，生产者消费者之间互斥关系
        pthread_mutex_lock(&_mutex);
        while(IsEmpty())
        {
            //条件变量确保了生产者和消费者之间同步关系
            //当阻塞队列为满,必须让消费者先读取数据、当阻塞队列为空,必须让生产者先写入数据
            ++_wait_consumer;
            pthread_cond_wait(&_consumer_cond, &_mutex);
            --_wait_consumer;
        }
        //进行消费
        *out = _block_queue.front();
        _block_queue.pop();
        //有位置了,生产者可以生产数据了
        if(_wait_producer > 0)
            pthread_cond_signal(&_producer_cond);
        pthread_mutex_unlock(&_mutex);
    }

private:
    std::queue<T> _block_queue; //阻塞队列,共享资源
    int _cap; //最大容量
    pthread_mutex_t _mutex; //保护_block_queue的互斥量
    pthread_cond_t _producer_cond; //专门提供给生产者的条件变量
    pthread_cond_t _consumer_cond;  //专门提供给消费者的条件变量

    int _wait_producer;  //生产者等待数
    int _wait_consumer; //消费者等待数
};